// -*- C++ -*-

using namespace ndk;

inline 
int ndk_os::vsnprintf(char *buf, size_t maxlen, const char *format, va_list ap)
{
  int result = 0;
#if defined NDK_LINUX
  result = ::vsnprintf(buf, maxlen, format, ap);
#elif defined NDK_WIN32
  result = ::_vsnprintf(buf, maxlen, format, ap);
  // Win32 doesn't regard a full buffer with no 0-terminate as an overrun.
  if (result == static_cast<int>(maxlen))
    buf[maxlen-1] = '\0';
  // Win32 doesn't 0-terminate the string if it overruns maxlen.
  if (result == -1)
    buf[maxlen-1] = '\0';
#endif
  // In out-of-range conditions, C99 defines vsnprintf() to return the number     
  // of characters that would have been written if enough space was available. 
  // Earlier variants of the vsnprintf() (e.g. UNIX98) defined it to return 
  // -1. This method follows the C99 standard, but needs to guess at the  
  // value; uses maxlen + 1. 
  if (result == -1)                                                                   
  {
    result = static_cast <int> (maxlen + 1);                                        
  }

  return result;
}
inline
int ndk_os::snprintf(char *buf, size_t maxlen, const char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  int result = ndk_os::vsnprintf(buf, maxlen, format, ap);
  va_end(ap);
  return result;
}
inline 
int ndk_os::last_error()
{
#if defined NDK_WIN32
  int lerror = ::GetLastError ();
  int lerrno = errno;
  return lerrno == 0 ? lerror : lerrno;
#else
  return errno;
#endif
}
inline 
void ndk_os::last_error(int err)
{
#if defined NDK_WIN32
  ::SetLastError (error);
#else
  errno = err;
#endif
}
inline
int ndk_os::exists(const char *filename)
{
#if defined NDK_LINUX
  return ::access(filename, F_OK) == 0 ? 1 : 0;
#elif defined NDK_WIN32
  return ::_access(filename, 0) == 0 ? 1 : 0;
#endif
  return -1;
}
inline
int ndk_os::bind(ndk_handle handle,
                 struct sockaddr *addr,
                 int addrlen)
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::bind(handle,
                                    addr,
                                    addrlen),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::bind(handle,
                addr,
                static_cast<socklen_t>(addrlen));
#endif
}
inline
int ndk_os::listen(ndk_handle handle,
                   int backlog)
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::listen(handle,
                                      backlog),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::listen(handle,
                  backlog);
#endif
}
inline
ndk_handle ndk_os::accept(ndk_handle listen_handle,
                          struct sockaddr *addr,
                          int *addrlen)
{
#if defined NDK_WIN32
  return NKD_SOCKCALL_RETURN(::accept(listen_handle,
                                      addr,
                                      addrlen),
                             ndk_handle, 
                             NDK_INVALID_HANDLE);
#elif defined NDK_LINUX
  return ::accept(listen_handle, 
                  addr, 
                  reinterpret_cast<socklen_t*>(addrlen));
#endif
}
inline
int ndk_os::connect(ndk_handle handle,
                    const struct sockaddr *addr,
                    int addrlen)
{
#if defined NDK_WIN32
  return NKD_SOCKCALL_RETURN(::connect(handle,
                                       addr,
                                       addrlen),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::connect(handle,
                   addr,
                   static_cast<socklen_t>(addrlen));
#endif
}
inline
ndk_handle ndk_os::socket(int domain, int type, int protocol)
{
  return ::socket(domain, type, protocol);
}
inline
int ndk_os::closesocket(ndk_handle handle)
{
#if defined NDK_WIN32
  return ::closesocket(handle);
#elif defined NDK_LINUX
  return ::close(handle);
#endif
}
inline
int ndk_os::setsock_reuseaddr(ndk_handle handle, int val)
{
  return ::setsockopt(handle, 
                      SOL_SOCKET, 
                      SO_REUSEADDR, 
                      (const void*)&val,
                      sizeof(val));
}
inline
int ndk_os::setsock_rcvbuf(ndk_handle handle, size_t size)
{
  if (size == 0) return -1;
  return ::setsockopt(handle, 
                      SOL_SOCKET, 
                      SO_RCVBUF, 
                      (const void*)&size, 
                      sizeof(size));
}
inline
int ndk_os::setsock_sndbuf(ndk_handle handle, size_t size)
{
  if (size == 0) return -1;
  return ::setsockopt(handle, 
                      SOL_SOCKET, 
                      SO_SNDBUF, 
                      (const void*)&size, 
                      sizeof(size));
}
inline
int ndk_os::getsock_error(ndk_handle handle, int &err)
{
#if defined NDK_WIN32
  int len = sizeof(int);
  return NDK_SOCKCALL_RETURN(::getsockopt(handle,
                                          SOL_SOCKET,
                                          SO_ERROR,
                                          &err,
                                          &len),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  socklen_t len = sizeof(int);
  return ::getsockopt(handle,
                      SOL_SOCKET,
                      SO_ERROR,
                      &err,
                      &len);
#endif
}
inline
int ndk_os::recv(ndk_handle handle,
                 void *buff,
                 size_t len,
                 int flags)
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::recv(handle, buff, len, flags),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::recv(handle, buff, len, flags);
#endif
}
inline
int ndk_os::recvfrom(ndk_handle handle,
                     void *buff,
                     size_t len,
                     int flags,
                     struct sockaddr *from, 
                     int *fromlen)
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::recv(handle, buff, 
                                    static_cast<int>(len), flags,
                                    from, fromlen),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::recvfrom(handle, buff, len, flags, from,(socklen_t *)fromlen);
#endif
}
inline
int ndk_os::send(ndk_handle handle, 
                 const void *buff, 
                 size_t len, 
                 int flags) 
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::send(handle, buff, len, flags),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::send(handle, buff, len, flags);
#endif
}
inline
int ndk_os::sendto(ndk_handle handle, 
                   const void *buff, 
                   size_t len, 
                   int flags, 
                   const struct sockaddr *to, 
                   int tolen)
{
#if defined NDK_WIN32
  return NDK_SOCKCALL_RETURN(::sendto(handle, buff, 
                                      static_cast<int>(len), flags, to, tolen),
                             int,
                             SOCKET_ERROR);
#elif defined NDK_LINUX
  return ::sendto(handle, buff, len, flags, to, tolen);
#endif
}
inline
int ndk_os::open(const char *filename,
                 int mode,
                 mode_t perms)
{
#if defined NDK_WIN32
  DWORD access = GENERIC_READ;
  if (NDK_BIT_ENABLED(mode, O_WRONLY))
    access = GENERIC_WRITE;
  else if (NDK_BIT_ENABLED(mode, O_RDWR))
    access = GENERIC_READ | GENERIC_WRITE;
  DWORD creation = OPEN_EXISTING;
  if ((mode &(O_CREAT | O_EXCL)) ==(O_CREAT | O_EXCL))
    creation = CREATE_NEW;
  else if ((mode &(O_CREAT | O_TRUNC)) ==(O_CREAT | O_TRUNC))
    creation = CREATE_ALWAYS;
  else if (NDK_BIT_ENABLED(mode, O_CREAT))
    creation = OPEN_ALWAYS;
  else if (NDK_BIT_ENABLED(mode, _O_TRUNC))
    creation = TRUNCATE_EXISTING;

  DWORD flags = FILE_FLAG_SEQUENTIAL_SCAN;
  DWORD shared_mode = perms;
  SECURITY_ATTRIBUTES sa;
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;
  ndk_handle h = ::CreateFile(filename,
                              access,
                              shared_mode,
                              &sa,
                              creation,
                              flags,
                              0);
  if (NDK_BIT_ENABLED(mode, O_APPEND))
  {
    LONG high_size = 0;
    if (h != NDK_INVALID_HANDLE
        && ::SetFilePointer(h,
                            0,
                            &high_size,
                            FILE_END) == INVALID_SET_FILE_POINTER
        && GetLastError() != NO_ERROR)
    {
      return NDK_INVALID_HANDLE;
    }
  }
  return h;
#elif defined NDK_LINUX
  return ::open(filename, mode, perms);
#endif
}
inline
int ndk_os::open(const char *filename,
                 int flags)
{
#if defined NDK_WIN32
  ;
#elif defined NDK_LINUX
  return ndk_os::open(filename, flags, 0);
#endif
}
inline
int ndk_os::read(ndk_handle handle,
                 void *buff,
                 size_t len)
{
#if defined NDK_WIN32
  return -1;
#elif defined NDK_LINUX
  return ::read(handle, buff, len);
#endif
}
inline
int ndk_os::write(ndk_handle handle,
                  const void *buff,
                  size_t len)
{
#if defined NDK_WIN32
  return -1;
#elif defined NDK_LINUX
  return ::write(handle, buff, len);
#endif
}
inline
int ndk_os::readv(ndk_handle handle,
                  iovec iov[],
                  size_t count)
{
#if defined NDK_WIN32
  DWORD flags = 0;
  DWORD bytes_received = 0;
  int result = ::WSARecv((SOCKET) handle,
                         (WSABUF *) iov,
                         count,
                         &bytes_received,
                         &flags,
                         0,
                         0);
  if (result == SOCKET_ERROR)
    return -1;
  return (int)bytes_received;
#elif defined NDK_LINUX
  return ::readv(handle, iov, count);
#endif
}
inline
int ndk_os::writev(ndk_handle handle,
                   const iovec iov[],
                   size_t count)
{
#if defined NDK_WIN32
  DWORD bytes_sent = 0;
  int result = ::WSASend((SOCKET) handle,
                         (WSABUF *) iov,
                         count,
                         &bytes_sent,
                         0,
                         0,
                         0);
  if (result == SOCKET_ERROR)
    return -1;
  return (int)bytes_sent;
#elif defined NDK_LINUX
  return ::writev(handle, iov, count);
#endif
}
inline
int ndk_os::close(ndk_handle handle)
{
  return ::close(handle);
}
inline
int ndk_os::stat(const char *name,
                 struct stat *st_buf)
{
#if defined NDK_WIN32
  return ::_stat(name, st_buf);
#elif defined NDK_LINUX
  return ::stat(name, st_buf);
#endif
}
inline
int ndk_os::mkdir(const char *name,
                  mode_t type)
{
#if defined NDK_WIN32
  unused_arg(type);
  ::_mkdir(name);
#elif defined NDK_LINUX
  return ::mkdir(name, type);
#endif
}
inline
int ndk_os::rename(const char *old_name,
                   const char *new_name)
{
#if defined NDK_WIN32
  int flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
  if (::MoveFileEx(old_name, new_name, flags) == 0)
    return -1;
#elif defined NDK_LINUX
  return ::rename(old_name, new_name);
#endif
}
inline
int ndk_os::unlink(const char *filename)
{
#if defined NDK_WIN32
  if (::Remove(filename) == 0)
    return -1;
#elif defined NDK_LINUX
  return ::unlink(filename);
#endif
}
